package cn.xiaolongyuan.spring.cloud.swagger2.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.swagger.web.SwaggerResource;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
@Slf4j
public class Swagger2Service {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate serviceRestTemplate;

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${swagger.name.prefix:}")
    private String prefix;

    @Value("${swagger.name.exclude:}")
    private String exclude;


    LoadingCache<String, JSONArray> cache = Caffeine.newBuilder()
            .maximumSize(10_00)
            .scheduler(Scheduler.systemScheduler())
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .refreshAfterWrite(30, TimeUnit.SECONDS)
            .build(new CacheLoader<String, JSONArray>() {
                @Nullable
                @Override
                public JSONArray load(@NonNull String s) throws Exception {
                    log.info("刷新Swagger文档：{}", s);
                    return fetchSwaggerResources(s);
                }
            });


    public List<SwaggerResource> fetchAll() {
        Map<String, SwaggerResource> resourceMap = Maps.newConcurrentMap();
        discoveryClient.getServices().parallelStream()
                .filter(s -> {
                    if(StringUtils.isNotBlank(prefix)){
                        String[] strings = StringUtils.split(prefix,",");
                        return Stream.of(strings).filter(key ->  StringUtils.startsWith(s,key)).findFirst().isPresent();
                    }
                    return true;
                })
                .filter(s -> !s.equals(applicationName))
                .filter(s -> {
                    if(StringUtils.isNotBlank(exclude)){
                        String[] strings = StringUtils.split(exclude,",");
                        return !ArrayUtils.contains(strings,s);
                    }
                    return true;
                })
                .forEach(serviceName -> {
                    JSONArray array = cache.get(serviceName);
                    if (!CollectionUtils.isEmpty(array)) {
                        for (int i = 0; i < array.size(); i++) {
                            JSONObject item = array.getJSONObject(i);
                            String name = serviceName + ":" + item.getString("name");
                            String location = "/" + serviceName + item.getString("location");
                            String url = item.getString("url");
                            String v = item.getString("swaggerVersion");
                            SwaggerResource resource = swaggerResource(name, location, url, v);
                            resourceMap.put(name, resource);
                        }
                    }
                });
        return resourceMap.keySet().stream().sorted()
                .map(key -> resourceMap.get(key))
                .collect(Collectors.toList());
    }


    private SwaggerResource swaggerResource(String name, String location, String url, String version) {
        SwaggerResource swaggerResource = new SwaggerResource();
        swaggerResource.setName(name);
        swaggerResource.setLocation(location);
        swaggerResource.setSwaggerVersion(version);
//        swaggerResource.setUrl(url);
        return swaggerResource;
    }

    public JSONArray fetchSwaggerResources(String name) {
        try {
            String json = serviceRestTemplate.getForObject(String.format("http://%s/swagger-resources", name), String.class);
            return JSON.parseArray(json);
        } catch (Exception e) {
            log.info("未找到文档配置：{}", e.getMessage());
        }
        return null;
    }

    @Scheduled(fixedDelay = 30 * 1000L, initialDelay = 15 * 1000L)
    public void init() {
        fetchAll();
    }

}
